package com.thtfpc.ssdp;

import java.io.UnsupportedEncodingException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;

/**
 * <b>SSDPMessage 类用来装载、解析 SSDP协议中设备之间交互的信息</b>
 *
 * <p>1.SSDPMessage的解析：</p>
 * 解析功能主要依靠静态方法 parseFrom，以字符串或者字节数组的形式传入ssdp消息体，完成对目标的分析
 * <p>
 * SSDPMessage message = SSDPMessage.parseFrom(...);
 * </p>
 *
 * <p>2.SSDPMessage的构造: </p> *
 * SSDPMessage message = new SSDPMessage(String:messageType);
 * message.addHeader("Cache-Control", "no-cache");
 *
 * <p>3.SSDPMessage 获取字节流，以便发送</p> *
 * message.toByteArray();
 *
 */
public class SSDPMessage {

    /**
     * 对SSDP消息的header键值对中的key进行存储
     *
     * SSDP消息头中的header的key是不区分大小写的，Cache-Control和CACHE-CONTROL代表同样的key；
     * Map对String的存储是区分大小写的，所以使用HeaderKey，对key进行封装套壳。
     * HeaderKey对象重写equals方法， 内部使用String.equalsIgnoreCast进行keyString对比
     */
    public static class HeaderKey {
        protected String headerKeyString;

        public HeaderKey(String keyString) {
            this.headerKeyString = keyString;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            HeaderKey headerKey = (HeaderKey) o;
            return headerKeyString.equalsIgnoreCase(headerKey.headerKeyString);
        }

        @Override
        public int hashCode() {
            return Objects.hash(headerKeyString.toUpperCase());
        }

        @Override
        public String toString() {
            return this.headerKeyString;
        }
    }

    public static String SSDP_SEARCH_HEADER = "M-SEARCH * HTTP/1.1";
    public static String SSDP_RESPONSE_OK_HEADER = "HTTP/1.1 200 OK";
    public static String SSDP_NOTIFY_HEADER = "NOTIFY * HTTP/1.1";


    /**
     * 装载所有的SSDP消息体的参数
     * Map的Key被设计为HeaderKey，用来在不区分大小写的模式下对Key进行匹配
     */
    private Map<HeaderKey, String> ssdpHeaders;

    /**
     *  * messageType 用来装载信息的基本类型，包括：
     *  * <p><B>M-SEARCH * HTTP/1.1</B></p> 主动查询消息头
     *  * <p><B>NOTIFY * HTTP/1.1</B></p> 主动声明消息头
     *  * <p><B>NOTIFY * HTTP/1.1"</B></p> 回应信息消息头
     */
    private String messageType;


    /**
     * 用来装载SSDPMessage对端的ip地址
     */
    private String address;


    /**
     * 用来装载SSDPMessage对端ip地址端口号
     */
    private int port;


    public SSDPMessage(String messageType) {
        this.messageType = messageType;
        this.ssdpHeaders = new LinkedHashMap<>();
    }

    public void setMessageType(String messageType) {
        this.messageType = messageType;
    }

    public String getMessageType() {
        return messageType;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getAddress() {
        return this.address;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public boolean containHeader(String headerKey) {
        return ssdpHeaders.containsKey(new HeaderKey(headerKey));
    }

    public String getHeader(String headerKey) {
        HeaderKey key = new HeaderKey(headerKey);
        if (ssdpHeaders.containsKey(key)) {
            return ssdpHeaders.get(key);
        }
        return null;
    }

    public void addHeader(String key, String value) {
        ssdpHeaders.put(new HeaderKey(key), value);
    }

    public byte[] toByteArray() {
        StringBuilder builder = new StringBuilder();
        if (messageType != null && messageType.trim() != "") {
            builder.append(messageType);
            builder.append("\r\n");
        }
        for (HeaderKey key : ssdpHeaders.keySet()) {
            builder.append(key.toString());
            builder.append(": ");
            builder.append(ssdpHeaders.get(key));
            builder.append("\r\n");
        }
        builder.append("\r\n");
        try {
            return builder.toString().getBytes("utf8");
        }
        catch (UnsupportedEncodingException ex) {
        }
        return builder.toString().getBytes();
    }

    @Override
    public String toString() {
        return "SSDPMessage{" +
                "ssdpHeaders=" + ssdpHeaders +
                ", messageType='" + messageType + '\'' +
                ", address='" + address + '\'' +
                ", port=" + port +
                '}';
    }

    public static SSDPMessage parseFrom(byte[] buffer, int offset, int length) {
        try {
            return parseFrom(new String(buffer, offset, length, "utf8"));
        }
        catch (UnsupportedEncodingException ex) {
        }
        return parseFrom(new String(buffer, offset, length));
    }

    public static SSDPMessage parseFrom(String messageString) {
        if (messageString.endsWith("\r\n\r\n") == false && messageString.indexOf("\r\n\r\n") == -1) {
            return null;
        }
        String[] messageLines = messageString.split("\r\n\r\n")[0].split("\r\n");
        SSDPMessage message = null;
        for (int i = 0; i < messageLines.length; i++) {
            String line = messageLines[i];
            if (i == 0) {
                if (line.indexOf(":") == -1) {
                    message = new SSDPMessage(line);
                }
                else {
                    message = new SSDPMessage(null);
                }
            }
            else {
                String[] parameters = line.split(": ");
                if (parameters.length == 2) {
                    message.addHeader(parameters[0], parameters[1].trim());
                }
            }
        }
        return message;
    }
}
